package com.dd.cloud.user.service.order.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dd.cloud.user.config.OrderPrefix;
import com.dd.cloud.common.constants.OrderConstants;
import com.dd.cloud.user.entity.order.OrderFlow;
import com.dd.cloud.user.entity.order.OrderPay;
import com.dd.cloud.user.entity.product.PayParam;
import com.dd.cloud.user.enums.ExceptionEnum;
import com.dd.cloud.user.mapper.order.OrderFlowMapper;
import com.dd.cloud.user.req.order.CreateFlowReq;
import com.dd.cloud.user.res.order.PayOrderResponse;
import com.dd.cloud.user.service.order.IOrderFlowService;
import com.dd.cloud.user.service.product.IPayParamService;
import com.dd.cloud.common.config.common.SpringContextJobUtil;
import com.dd.cloud.common.enums.ZkExceptionEnum;
import com.dd.cloud.common.exception.ZekeException;
import com.dd.cloud.common.utils.JsonUtils;
import com.dd.cloud.common.utils.SerialNumberUtils;
import com.zk.pay.channel.IPaymentService;
import com.zk.pay.channel.IRefundService;
import com.zk.pay.model.MchInfoConfigContext;
import com.zk.pay.rqrs.AbstractRS;
import com.zk.pay.rqrs.msg.ChannelRetMsg;
import com.zk.pay.rqrs.payorder.UnifiedOrderRS;
import com.zk.pay.rqrs.refund.RefundOrderRQ;
import com.zk.pay.util.RequestKitBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 订单流水表 服务实现类
 * </p>
 *
 * @author czg
 * @since 2022-09-03
 */
@Service
@Slf4j
public class OrderFlowServiceImpl extends ServiceImpl<OrderFlowMapper, OrderFlow> implements IOrderFlowService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private OrderPrefix orderPrefix;
    @Autowired
    private IPayParamService payParamService;
    @Autowired
    private RequestKitBean requestKitBean;

    /**
     * 订单流水退款
     *
     * @param orderPay
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(OrderPay orderPay) {
        QueryWrapper<OrderFlow> qw = new QueryWrapper<>();
        qw.lambda().eq(OrderFlow::getOrderNo, orderPay.getOrderNo())
                .eq(OrderFlow::getStatus, OrderConstants.FlowStatus.PAY);
        List<OrderFlow> flowList = list(qw);
        PayParam payParam = payParamService.getById(orderPay.getPayParamId());
        if (payParam == null) {
            throw new ZekeException(ZkExceptionEnum.REFUND_ERROR, "查询支付参数失败");
        }
        String payType = "wx";
        if (!payParam.getPayType().startsWith("wx")){
            payType = payParam.getPayType();
        }
        payParam.setPayType(payType);
        for (OrderFlow flow : flowList) {
            OrderFlow flowRefund = new OrderFlow();
            //生成唯一订单流水编号
            String flowNo = SerialNumberUtils.getInstance().generateSerialNumber(redisTemplate, orderPrefix.getFlowRefundPrefix());
            flowRefund.setFlowNo(flowNo);
            flowRefund.setFlowType(OrderConstants.FlowType.REFUND);
            flowRefund.setOrderNo(orderPay.getOrderNo());
            flowRefund.setCreateDate(LocalDateTime.now());
            flowRefund.setStatus(OrderConstants.FlowStatus.NOPAY);
            flowRefund.setPayment(flow.getPayment());
            flowRefund.setTotalFee(flow.getTotalFee());
            flowRefund.setStatus(OrderConstants.FlowStatus.NOPAY);
            boolean flag = save(flowRefund);
            if (!flag) {
                throw new ZekeException(ZkExceptionEnum.REFUND_ERROR, "退款流水添加失败");
            }
            /**
             * 退款
             */
            IRefundService refundService = SpringContextJobUtil.getBean(payParam.getPayType() + "payRefundService", IRefundService.class);
            RefundOrderRQ rq = new RefundOrderRQ();
            rq.setMchOrderNo(flow.getFlowNo());
            rq.setMchRefundNo(flowNo);
            rq.setAmount(flow.getTotalFee().longValue());
            rq.setRefundAmount(flow.getTotalFee().longValue());
            rq.setAppId(payParam.getAppId());
            rq.setNotifyUrl(payParam.getRefundNotify() + payParam.getAppId() + "_" + payParam.getId());
            ChannelRetMsg msg;
            MchInfoConfigContext mchInfoConfigContext = requestKitBean.getMchInfoConfigContext(payParam.getAppId() + "_" + payParam.getId());
            if (mchInfoConfigContext == null) {
                throw new ZekeException(ZkExceptionEnum.REFUND_ERROR, "支付参数异常,请联系管理员");
            }
            try {
                msg = refundService.refund(rq, mchInfoConfigContext);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ZekeException(ZkExceptionEnum.REFUND_ERROR, e.getMessage());
            }
            log.info(msg.toString());
            if (!StringUtils.isBlank(msg.getChannelErrCode())) {
                throw new ZekeException(ZkExceptionEnum.REFUND_ERROR, msg.getChannelErrMsg());
            }
        }
    }

    /**
     * 创建支付流水
     *
     * @param request
     * @param req
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayOrderResponse createFlow(HttpServletRequest request, CreateFlowReq req, PayParam payParam) {
        OrderFlow flow = new OrderFlow();
        //生成唯一订单流水编号
        String flowNo = SerialNumberUtils.getInstance().generateSerialNumber(redisTemplate, orderPrefix.getFlowPrefix());
        flow.setFlowNo(flowNo);
        flow.setOrderNo(req.getOrderNo());
        flow.setOpenid(req.getOpenid());
        flow.setTotalFee(req.getPrice());
        flow.setFlowType(OrderConstants.FlowType.PAY);
        flow.setCreateDate(LocalDateTime.now());
        flow.setStatus(OrderConstants.FlowStatus.NOPAY);
        flow.setPayment(req.getPayType());
        int flag = getBaseMapper().insert(flow);
        if (flag <= 0) {
            throw new ZekeException(ExceptionEnum.PRES_CREATE_ERROR, "订单流水创建失败");
        }
        String payType = "wx";
        if (!payParam.getPayType().startsWith("wx")){
            payType = payParam.getPayType();
        }
        payParam.setPayType(payType);
        /**
         * 调起支付
         */
        PayOrderResponse response = new PayOrderResponse();
        IPaymentService paymentService = SpringContextJobUtil.getBean(payParam.getPayType() + "payPaymentService", IPaymentService.class);
        MchInfoConfigContext mchInfoConfigContext = new MchInfoConfigContext(payParam.payParam());
        AbstractRS rs;
        try {
            rs = paymentService.pay(req.params(request, payParam, flow), mchInfoConfigContext);
            /**
             * 同一个appid会绑定多个商户号,所以不能用appid做key, 这里用appid+id 并设置有效期
             */
            redisTemplate.boundValueOps(payParam.payParam().getAppId() + "_" + payParam.getId())
                    .set(JsonUtils.objectToStr(payParam.payParam()));

        } catch (Exception e) {
            e.printStackTrace();
            throw new ZekeException(ZkExceptionEnum.PAY_ERROR, e.getMessage());
        }
        UnifiedOrderRS orderRS = (UnifiedOrderRS) rs;
        ChannelRetMsg msg = orderRS.getChannelRetMsg();
        if (!StringUtils.isBlank(msg.getChannelErrCode())) {
            throw new ZekeException(ZkExceptionEnum.PAY_ERROR, msg.getChannelErrMsg());
        }
        response.setOrderFlow(flow);
        response.setPay(rs.buildPayData());
        return response;
    }

    /**
     * 重新支付流水
     *
     * @param request
     * @param flowReq
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayOrderResponse payUnpayFlow(HttpServletRequest request, CreateFlowReq flowReq, PayParam payParam) {
        PayOrderResponse payOrderResponse = new PayOrderResponse();
        OrderFlow orderFlow = getBaseMapper().selectOne(new QueryWrapper<OrderFlow>().lambda().eq(OrderFlow::getOrderNo, flowReq.getOrderNo()).last("limit 1"));
        if (orderFlow == null) {
            throw new ZekeException(ZkExceptionEnum.PARAMETER_ERROR, "流水不存在");
        }
        if (orderFlow.getStatus() == OrderConstants.FlowStatus.PAY) {
            throw new ZekeException(ExceptionEnum.ORDER_FLOW_IS_PAY);
        }
        String flowNo = SerialNumberUtils.getInstance().generateSerialNumber(redisTemplate, orderPrefix.getFlowPrefix());
        int i = getBaseMapper().updateFlowNo(orderFlow.getFlowNo(), flowNo, flowReq.getPrice(), flowReq.getPayType(), flowReq.getOpenid());
        if (i <= 0) {
            throw new ZekeException(400, "请重试");
        }
        String payType = "wx";
        if (!payParam.getPayType().startsWith("wx")){
            payType = payParam.getPayType();
        }
        payParam.setPayType(payType);
        orderFlow.setFlowNo(flowNo);
        orderFlow.setOpenid(flowReq.getOpenid());
        orderFlow.setTotalFee(flowReq.getPrice());
        orderFlow.setPayment(flowReq.getPayType());
        IPaymentService paymentService = SpringContextJobUtil.getBean(payParam.getPayType() + "payPaymentService", IPaymentService.class);
        MchInfoConfigContext mchInfoConfigContext = new MchInfoConfigContext(payParam.payParam());
        AbstractRS rs;
        try {
            rs = paymentService.pay(flowReq.params(request, payParam, orderFlow), mchInfoConfigContext);
            /**
             * 同一个appid会绑定多个商户号,所以不能用appid做key, 这里用appid+id 并设置有效期
             */
            redisTemplate.boundValueOps(payParam.payParam().getAppId() + "_" + payParam.getId()).set(JsonUtils.objectToStr(payParam.payParam()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new ZekeException(ZkExceptionEnum.PAY_ERROR, e.getMessage());
        }
        UnifiedOrderRS orderRS = (UnifiedOrderRS) rs;
        ChannelRetMsg msg = orderRS.getChannelRetMsg();
        if (!StringUtils.isBlank(msg.getChannelErrCode())) {
            throw new ZekeException(ZkExceptionEnum.PAY_ERROR, msg.getChannelErrMsg());
        }
        payOrderResponse.setOrderFlow(orderFlow);
        payOrderResponse.setPay(rs.buildPayData());
        return payOrderResponse;
    }

    /**
     * 获取流水信息
     *
     * @param flowNo
     * @return
     */
    @Override
    public OrderFlow getOrderFlow(String flowNo) {
        return getById(flowNo);
    }
}
